﻿namespace Blaze.Framework
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Threading;
    using Logging;

    /// <summary>
    /// 主线程执行管道。
    /// </summary>
    public static class MainThreadFiber
    {
        /// <summary>
        /// 获取一个值，表示当前是否处于主线程中。
        /// </summary>
        public static bool IsInMainThread
        {
            get { return Thread.CurrentThread.ManagedThreadId == mMainThreadId; }
        }

        /// <summary>
        /// 清除队列中所有需要执行的方法。
        /// </summary>
        public static void Clear()
        {
            lock (mLockTarget)
            {
                mPendingActions.Clear();
            }
        }

        /// <summary>
        /// 确保在主线程中执行。
        /// </summary>
        public static void EnsureContext()
        {
            if (!IsInMainThread)
                throw new InvalidOperationException();
        }

        /// <summary>
        /// 在主线程中执行指定的方法，若已经在主线程中，则会立即执行，否则会推入队列中，在下次更新时调用。
        /// </summary>
        /// <param name="action">需要执行的方法</param>
        public static void Execute(Action action)
        {
            if (IsInMainThread)
            {
                try
                {
                    action();
                }
                catch (Exception e)
                {
                    mLog.ErrorFormat("execute failed -> {0} Stack={1}", e.Message, e.StackTrace);
                }
                return;
            }

            lock (mLockTarget)
            {
                mPendingActions.Enqueue(action);
            }
        }

        /// <summary>
        /// 初始化执行管道，需要在主线程中执行。
        /// </summary>
        public static void Initialize()
        {
            if (mIsInitialized)
                return;
            mMainThreadId = Thread.CurrentThread.ManagedThreadId;
            CoroutineManager.StartNew(update());
            mIsInitialized = true;
        }

        private static IEnumerator update()
        {
            while (true)
            {
                lock (mLockTarget)
                {
                    while (mPendingActions.Count > 0)
                        mDequeuedActions.Add(mPendingActions.Dequeue());
                }
                for (var i = 0; i < mDequeuedActions.Count; i++)
                {
                    try
                    {
                        mDequeuedActions[i]();
                    }
                    catch (Exception e)
                    {
                        mLog.ErrorFormat("execute failed -> {0} Stack={1}", e.Message, e.StackTrace);
                    }
                }
                mDequeuedActions.Clear();
                yield return null;
            }
        }

        private static int mMainThreadId;
        private static readonly Queue<Action> mPendingActions = new Queue<Action>();
        private static readonly List<Action> mDequeuedActions = new List<Action>(8);
        private static readonly object mLockTarget = new object();
        private static readonly Log mLog = LogManager.GetLogger(typeof (MainThreadFiber));
        private static bool mIsInitialized;
    }
}